package com.immediatus.graphics.managers;

import com.immediatus.graphics.buffer.BufferObject;

import javax.microedition.khronos.opengles.GL11;
import java.util.ArrayList;
import java.util.HashSet;

public class BufferObjectManager{

    private static final HashSet<BufferObject> _bufferObjectsManaged = new HashSet<BufferObject>();
    private static final ArrayList<BufferObject> _bufferObjectsLoaded = new ArrayList<BufferObject>();
    private static final ArrayList<BufferObject> _bufferObjectsToBeLoaded = new ArrayList<BufferObject>();
    private static final ArrayList<BufferObject> _bufferObjectsToBeUnloaded = new ArrayList<BufferObject>();

    private static BufferObjectManager _activeInstance = null;

    public static BufferObjectManager getActiveInstance(){
        return BufferObjectManager._activeInstance;
    }

    public static void setActiveInstance(final BufferObjectManager _activeInstance){
        BufferObjectManager._activeInstance = _activeInstance;
    }

    public synchronized void clear(){
        BufferObjectManager._bufferObjectsToBeLoaded.clear();
        BufferObjectManager._bufferObjectsLoaded.clear();
        BufferObjectManager._bufferObjectsManaged.clear();
    }

    public synchronized void loadBufferObject(final BufferObject _bufferObject){
        if (_bufferObject == null){
            return;
        }

        if (BufferObjectManager._bufferObjectsManaged.contains(_bufferObject)){
            BufferObjectManager._bufferObjectsToBeUnloaded.remove(_bufferObject);
        } else{
            BufferObjectManager._bufferObjectsManaged.add(_bufferObject);
            BufferObjectManager._bufferObjectsToBeLoaded.add(_bufferObject);
        }
    }

    public synchronized void unloadBufferObject(final BufferObject _bufferObject){
        if (_bufferObject == null){
            return;
        }
        if (BufferObjectManager._bufferObjectsManaged.contains(_bufferObject)){
            if (BufferObjectManager._bufferObjectsLoaded.contains(_bufferObject)){
                BufferObjectManager._bufferObjectsToBeUnloaded.add(_bufferObject);
            } else if (BufferObjectManager._bufferObjectsToBeLoaded.remove(_bufferObject)){
                BufferObjectManager._bufferObjectsManaged.remove(_bufferObject);
            }
        }
    }

    public void loadBufferObjects(final BufferObject... bufferObjects_){
        for (int i = bufferObjects_.length - 1; i >= 0; i--){
            this.loadBufferObject(bufferObjects_[i]);
        }
    }

    public void unloadBufferObjects(final BufferObject... bufferObjects_){
        for (int i = bufferObjects_.length - 1; i >= 0; i--){
            this.unloadBufferObject(bufferObjects_[i]);
        }
    }

    public synchronized void reloadBufferObjects(){
        final ArrayList<BufferObject> loadedBufferObjects = BufferObjectManager._bufferObjectsLoaded;
        for (int i = loadedBufferObjects.size() - 1; i >= 0; i--){
            loadedBufferObjects.get(i).setLoadedToHardware(false);
        }

        BufferObjectManager._bufferObjectsToBeLoaded.addAll(loadedBufferObjects);

        loadedBufferObjects.clear();
    }

    public synchronized void updateBufferObjects(final GL11 gl11_){
        final HashSet<BufferObject> bufferObjectsManaged = BufferObjectManager._bufferObjectsManaged;
        final ArrayList<BufferObject> bufferObjectsLoaded = BufferObjectManager._bufferObjectsLoaded;
        final ArrayList<BufferObject> bufferObjectsToBeLoaded = BufferObjectManager._bufferObjectsToBeLoaded;
        final ArrayList<BufferObject> bufferObjectsToBeUnloaded = BufferObjectManager._bufferObjectsToBeUnloaded;

        final int bufferObjectToBeLoadedCount = bufferObjectsToBeLoaded.size();

        if (bufferObjectToBeLoadedCount > 0){
            for (int i = bufferObjectToBeLoadedCount - 1; i >= 0; i--){
                final BufferObject bufferObjectToBeLoaded = bufferObjectsToBeLoaded.get(i);
                if (!bufferObjectToBeLoaded.isLoadedToHardware()){
                    bufferObjectToBeLoaded.loadToHardware(gl11_);
                    bufferObjectToBeLoaded.setHardwareBufferNeedsUpdate();
                }
                bufferObjectsLoaded.add(bufferObjectToBeLoaded);
            }

            bufferObjectsToBeLoaded.clear();
        }

        final int bufferObjectsToBeUnloadedCount = bufferObjectsToBeUnloaded.size();

        if (bufferObjectsToBeUnloadedCount > 0){
            for (int i = bufferObjectsToBeUnloadedCount - 1; i >= 0; i--){
                final BufferObject bufferObjectToBeUnloaded = bufferObjectsToBeUnloaded.remove(i);
                if (bufferObjectToBeUnloaded.isLoadedToHardware()){
                    bufferObjectToBeUnloaded.unloadFromHardware(gl11_);
                }
                bufferObjectsLoaded.remove(bufferObjectToBeUnloaded);
                bufferObjectsManaged.remove(bufferObjectToBeUnloaded);
            }
        }
    }
}
